<?php

namespace App\Kuafu\WaiMai\Channel;

use App\Kuafu\WaiMai\Enum\WaiMaiCacheEnum;
use App\Kuafu\WaiMai\Enum\WaiMaiDBEnum;
use App\Kuafu\WaiMai\Library\ElmDefineApi;
use App\Kuafu\WaiMai\Service\ElmWaiMaiOrderService;
use ElemeOpenApi\Api\FinanceService;
use ElemeOpenApi\Api\MsgNewService;
use ElemeOpenApi\Api\OrderService;
use ElemeOpenApi\Api\ProductService;
use ElemeOpenApi\Api\ShopService;
use ElemeOpenApi\Api\UserService;
use ElemeOpenApi\Config\Config;
use ElemeOpenApi\OAuth\OAuthClient;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class ElmWaiMai extends BaseWaiMai
{
    private string $appKey;
    private string $appSecret;
    private int    $thirdShopId;
    private        $token;
    private Config $config;
    private ?bool  $sandbox;

    // 应用id - 纯数字
    private string $appId;

    const SAND_BOX_THIRD_SHOP_ID = '503631946'; // 沙箱三方店铺id

    public function __construct(string $thirdShopId, int $shopId = 0, ?bool $sandbox = null)
    {
        parent::__construct();

        // 设置店铺
        $this->setThirdShopId($thirdShopId);
        if ($thirdShopId == self::SAND_BOX_THIRD_SHOP_ID) {
            $this->setSandbox(true);
            $this->appId = '57548186';
        } else {
            $this->setSandbox(false);
            $this->appId = '77911361';
        }

        // 加载配置
        $this->loadConfig();
    }

    /**
     * 设置thirdShopId
     * @param string $thirdShopId
     * @return $this
     */
    public function setThirdShopId(string $thirdShopId): self
    {
        $this->thirdShopId = (int)$thirdShopId;
        return $this;
    }

    /**
     * 设置沙箱环境
     * @param bool $sandbox
     * @return $this
     */
    public function setSandbox(bool $sandbox): self
    {
        $this->sandbox = $sandbox;
        return $this;
    }

    /**
     * 配置加载
     * @return $this
     */
    private function loadConfig()
    {
        if ($this->sandbox) {
            $this->appKey    = config('waimai.elm.sandbox.appId');
            $this->appSecret = config('waimai.elm.sandbox.appSecret');
        } else {
            $this->appKey    = config('waimai.elm.prod.appId');
            $this->appSecret = config('waimai.elm.prod.appSecret');
        }

        // 实例化一个配置类
        $this->config = new Config($this->appKey, $this->appSecret, $this->sandbox);
        $this->token = $this->getToken();

        return $this;
    }

    /**
     * 强刷token
     * @return $this
     */
    public function reloadToken(): self
    {
        $this->token = $this->getToken(self::FRESH_FORCE);

        return $this;
    }

    const FRESH_DEFAULT = 0; // 默认查询
    const FRESH_CHECK_TIME = 1; // 检查时间，并刷新
    const FRESH_FORCE = 2; // 强制刷新
    public function getToken(int $forceFresh = self::FRESH_DEFAULT)
    {
        $cacheKey = WaiMaiCacheEnum::getElmToken($this->appKey);
        if (Cache::has($cacheKey) && in_array($forceFresh, [self::FRESH_DEFAULT, self::FRESH_CHECK_TIME])) {
            $res = Cache::get($cacheKey);

            // 默认 - 拿到了就刷
            if ($forceFresh == self::FRESH_DEFAULT) {
                return $res;
            }

            // 检查时间，刚刷过就不刷了
            if ($forceFresh == self::FRESH_CHECK_TIME && $res->expiredAt - time() > 1000) {
                return $res;
            }
        }

        Log::info("ElmWaiMai getToken Request: {$forceFresh}");

        //使用config对象，实例化一个授权类
        $client = new OAuthClient($this->config);

        //使用授权类获取token
        $res = $client->get_token_in_client_credentials();
        if (!$res) {
            return null;
        }

        $addExpireTime = 1300;

        $res->expiredAt = time() + $addExpireTime;

        Log::info("ElmWaiMai getToken Response:", (array)$res);

        Cache::put($cacheKey, $res, $addExpireTime);

        return $res;
    }

    /**
     * 获取店铺信息
     * @return mixed
     */
    public function getShop()
    {
        if (!in_array($this->thirdShopId, $this->getShopIds()['data'])) {
            return $this->returnFormatData(false, '', "该thirdShopId:{$this->thirdShopId}未与饿了么开发者关联", []);
        }

        //使用config和token对象，实例化一个服务对象
        $shop_service = new ShopService($this->token, $this->config);

        try {
            $shop = $shop_service->get_shop($this->thirdShopId);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes         = $this->returnFormatData(true, '', '', $shop);
        $formatRes['data'] = [
            'shopId' => (int)($shop->openId ?? null),
            'name'   => $formatRes['oriData']['name'] ?? '',
            'phones' => $formatRes['oriData']['phones'] ?? [],
        ];

        return $formatRes;
    }

    /**
     * 映射店铺
     * @param int $shopId 己方的shopId
     * @return mixed
     */
    public function updateShop(?int $shopId)
    {
        //使用config和token对象，实例化一个服务对象
        $shop_service = new ShopService($this->token, $this->config);

        $properties = [
            'openId' => (string)$shopId,
        ];

        //调用服务方法，获取资源
        try {
            $shop = $shop_service->update_shop($this->thirdShopId, $properties);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        return $this->returnFormatData(true, '', '', $shop);
    }

    public function bindShop()
    {
        //使用config对象，实例化一个授权类
        $client = new OAuthClient($this->config);

        //根据OAuth2.0中的对应state，scope和callback_url，获取授权URL
        $state        = '156';
        $scope        = 'all';
        $callback_url = 'https://qa-b.51kuafu.com/api/pay/callback/elmWaiMai/push';
        $auth_url     = $client->get_auth_url($state, $scope, $callback_url);

        return $auth_url;
    }

    /**
     * 订单详情
     * @param int $orderId
     * @return array
     */
    public function getOrderDetail(int $orderId)
    {
        $order_service = new OrderService($this->token, $this->config);

        //调用服务方法，获取资源
        try {
            $shop = $order_service->get_order((string)$orderId);
        } catch (\Exception $e) {
            Log::channel('waiMai')->info("ELM getOrderDetail Err");
            Log::channel('waiMai')->info($e);
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }
        $formatRes         = $this->returnFormatData(true, '', '', $shop);
        $formatRes['data'] = [
            'orderId'     => $formatRes['oriData']['id'],
            'orderStatus' => ElmWaiMaiOrderService::convertOrderStatus($formatRes['oriData']['status']),
            'createdAt'   => strtotime($formatRes['oriData']['createdAt']),
        ];

        return $formatRes;
    }

    /**
     * 接单
     * @param int $orderId
     * @return array|mixed
     */
    public function acceptOrder(int $orderId)
    {
        $order_service = new OrderService($this->token, $this->config);

        try {
            $shop = $order_service->confirm_order_lite((string)$orderId);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        return $shop;
    }

    /**
     * 查询已绑定的店铺ids
     * @return array
     */
    public function getShopIds()
    {
        $user_service = new UserService($this->token, $this->config);

        try {
            $user = $user_service->get_user();
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes         = $this->returnFormatData(true, '', '', $user);
        $formatRes['data'] = array_column($formatRes['oriData']['authorizedShops'] ?? [], 'id');

        return $formatRes;
    }

    /**
     * 商家出餐
     * @param int $orderId
     * @return array|mixed
     */
    public function finishFoods(int $orderId)
    {
        $order_service = new OrderService($this->token, $this->config);

        try {
            $shop = $order_service->set_order_prepared((string)$orderId);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes = $this->returnFormatData(true, '', '', $shop);

        return $formatRes;
    }

    // 同意取消订单
    public function refundAgree(int $orderId)
    {
        $order_service = new OrderService($this->token, $this->config);

        try {
            $shop = $order_service->agree_refund_lite((string)$orderId);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes = $this->returnFormatData(true, '', '', $shop);

        return $formatRes;
    }

    // 不同意取消订单
    public function refundReject(int $orderId, string $reason)
    {
        $order_service = new OrderService($this->token, $this->config);

        try {
            $shop = $order_service->disagree_refund_lite((string)$orderId, $reason);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes = $this->returnFormatData(true, '', '', $shop);

        return $formatRes;
    }

    // 查询店铺未处理的订单
    public function getErrorOrders()
    {
        $order_service = new OrderService($this->token, $this->config);

        try {
            $shop = $order_service->get_unprocess_orders($this->thirdShopId);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes         = $this->returnFormatData(true, '', '', $shop);
        $formatRes['data'] = $formatRes['oriData']; // 未处理的orderId数组

        return $formatRes;
    }

    // 订单退款信息
    public function getRefundOrder(int $orderId)
    {
        $order_service = new OrderService($this->token, $this->config);

        try {
            $shop = $order_service->get_refund_order((string)$orderId);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        if (!isset($shop['orderId']) || $shop['orderId'] == null) {
            return $this->returnFormatData(false, '', '无退款信息', []);
        }

        $formatRes         = $this->returnFormatData(true, '', '', $shop);
        $formatRes['data'] = [
            'applyReason' => $shop['applyRefundReason'] ?? '', // elm一直返回空
            'applyImgs'   => json_encode($shop['refundImages'] ?? []),
            'refundType'  => ElmWaiMaiOrderService::convertRefundType($shop['refundType'] ?? 0),
            'refundPrice' => (int)bcmul($shop['totalPrice'] ?? 0, 100),
        ];

        return $formatRes;
    }

    // 查询全部订单
    public function getAllOrders(int $page, int $limit, int $date)
    {
        $order_service = new OrderService($this->token, $this->config);
        $date = date('Y-m-d', strtotime($date));

        try {
            $shop = $order_service->get_all_orders((int)$this->thirdShopId, $page, $limit, $date);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes = $this->returnFormatData(true, '', '', $shop);
        return $formatRes;
    }

    // 分页查询商品信息
    public function getProductPage(int $offset, int $limit)
    {
        if ($limit > 300) {
            $limit = 300;
        }

        $order_service = new ProductService($this->token, $this->config);
        try {
            $shop = $order_service->query_item_by_page(['shopId' => (int)$this->thirdShopId, 'offset' => $offset, 'limit' => $limit]);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $data = [];
        foreach ($shop as $v) {
            $data[] = [
                'id'         => $v->id,
                'categoryId' => $v->categoryId,
                'name'       => $v->name,
                'sourceType' => WaiMaiDBEnum::SOURCE_TYPE_ELM,
            ];
        }

        $formatRes = $this->returnFormatData(true, '', '', $shop);
        $formatRes['data'] = $data;
        return $formatRes;
    }

    public function isFinishFoods(int $orderId)
    {
        $order_service = new OrderService($this->token, $this->config);

        try {
            $shop = $order_service->get_prepared_times_by_order_ids([(string)$orderId]);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes = $this->returnFormatData(true, '', '', $shop);
        $formatRes['data'] = [
            'finish' => (int)(bool)($shop->$orderId ?? 0),
        ];

        return $formatRes;
    }

    public function finishTimeConfig()
    {
        if (config('app.env') == 'qa' || config('app.env') == 'dev') {
            if ($this->thirdShopId == self::SAND_BOX_THIRD_SHOP_ID) {
                return [
                    'status' => true,
                    'code' => '',
                    'msg' => '',
                    'data' => [
                        'promiseTime' => 300,
                    ],
                    'oriData' => [],
                ];
            }
        }

        $order_service = new OrderService($this->token, $this->config);

        try {
            $shop = $order_service->query_cooking_time_config($this->thirdShopId);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes         = $this->returnFormatData(true, '', '', $shop);
        $formatRes['data'] = [
            'promiseTime' => 60 * ($shop->cookingTimeConfig->commitTime ?? 0),
        ];

        return $formatRes;
    }

    /**
     * 外卖结算信息
     * @param int $orderId
     * @return array
     */
    public function refundAmountInfo(int $orderId): array
    {
        $order_service = new OrderService($this->token, $this->config);

        try {
            $shop = $order_service->query_refund_amount_info((string)$orderId);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes = $this->returnFormatData(true, '', '', $shop);

        $platformRefundPrice = ($formatRes['oriData']['originalPrice'] ?? 0) + ($formatRes['oriData']['packingBoxFee'] ?? 0) - ($formatRes['oriData']['merchantSubsidy'] ?? 0);

        $formatRes['data'] = [
            'platformRefundPrice' => (int)bcmul100($platformRefundPrice),
            'userReceivedAmount'  => (int)bcmul100($formatRes['oriData']['userReceivedAmount'] ?? 0),
            'packingBoxFee'  => (int)bcmul100($formatRes['oriData']['packingBoxFee'] ?? 0),
        ];

        return $formatRes;
    }

    /**
     * 订单金融信息
     * @param int $orderId
     * @return array
     */
    public function financeOrder(int $orderId)
    {
        $order_service = new ElmDefineApi($this->token, $this->config);

        try {
            $order = $order_service->get_finance_order((string)$orderId, $this->thirdShopId);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes = $this->returnFormatData(true, '', '', $order);

        $lists = collect($formatRes['oriData']['orders'] ?? [])->map(function ($item) {

            $checkoutAmount = $item['checkoutAmount'] ?? 0;
            if ($checkoutAmount == 0) {
                return null;
            }

            if ($checkoutAmount > 0) {
                $addIncomes    = $checkoutAmount;
                $reduceIncomes = 0;
            } else {
                $addIncomes    = 0;
                $reduceIncomes = $checkoutAmount;
            }

            return [
                'detailType'    => $item['detailType'],
                'addIncomes'    => (int)abs(bcmul100($addIncomes)),
                'reduceIncomes' => (int)abs(bcmul100($reduceIncomes)),
            ];
        })->filter()->values();

        $formatRes['data'] = [
            'addIncomes'    => $addIncomes = $lists->sum('addIncomes'),
            'reduceIncomes' => $reduceIncomes = $lists->sum('reduceIncomes'),
            'incomes'       => $addIncomes - $reduceIncomes,
            'lists'         => $lists,
        ];

        Log::channel('waiMai')->info("financeOrder orderId:{$orderId}", $formatRes);

        return $formatRes;
    }

    /**
     * 余额变化
     *
     * @param string|null $beginDate 年月日
     * @param string $endDate
     * @return mixed
     */
    public function queryBalanceLog(?string $beginDate, string $endDate = '')
    {
        $financeService = new FinanceService($this->token, $this->config);

        if(!$beginDate){
            $beginDate = date('Y-m-d', time());
        }

        $result = $financeService->query_balance_log([
            'beginDate' => (int)(strtotime($beginDate) . '000'),
            'pageSize' => 999,
            'shopId' => $this->thirdShopId
        ]);

        return $result;
    }

    /**
     * 获取未拉取的推送失败消息列表
     * @return array
     */
    public function getPushFailMsg()
    {
        $this->token->access_token = '';

        $msg_new_service = new MsgNewService($this->token, $this->config);

        try {
            $order = $msg_new_service->get_push_fail_msg(['appId' => $this->appId]);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes = $this->returnFormatData(true, '', '', $order);

        return $formatRes;
    }

    /**
     * ISV通过该接口向平台确认已成功拉取消息
     * @param $msgId string 消息id
     * @return array
     */
    public function confirmPullMsg(string $msgId)
    {
        $this->token->access_token = "";

        $msg_new_service = new MsgNewService($this->token, $this->config);
        $msg_confirm_request = array();
        $msg_confirm_request["appId"] = $this->appId;
        $msg_list = array();
        $msg_list_object = array();
        $msg_list_object["id"] = $msgId;
        $msg_list_object["appId"] = $this->appId;
        $msg_list[] = $msg_list_object;
        $msg_confirm_request["msgList"] = $msg_list;

        try {
            $order = $msg_new_service->confirm_pull_msg($msg_confirm_request);
        } catch (\Exception $e) {
            return $this->returnFormatData(false, $e->getCode(), $e->getMessage(), []);
        }

        $formatRes = $this->returnFormatData(true, '', '', $order);

        return $formatRes;
    }
}
